"""
风险管理服务
"""
import asyncio
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any
from dataclasses import dataclass

from ..core.config import settings
from ..core.database import db_manager

logger = logging.getLogger(__name__)


@dataclass
class RiskLimit:
    """风险限制"""
    limit_type: str
    current_value: float
    limit_value: float
    exceeded: bool
    message: str


@dataclass
class RiskAlert:
    """风险告警"""
    alert_type: str
    severity: str  # low/medium/high/critical
    message: str
    data: Dict[str, Any]
    timestamp: datetime


class RiskManager:
    """风险管理器"""
    
    def __init__(self):
        self.is_running = False
        self.check_task: Optional[asyncio.Task] = None
        self.risk_limits: Dict[str, float] = {}
        self.daily_stats: Dict[str, Any] = {}
        self.alerts: List[RiskAlert] = []
        self._lock = asyncio.Lock()
        
        # 初始化风险限制
        self._initialize_risk_limits()
    
    def _initialize_risk_limits(self):
        """初始化风险限制"""
        self.risk_limits = {
            "max_position_size": settings.MAX_POSITION_SIZE,
            "max_daily_loss": settings.MAX_DAILY_LOSS,
            "max_delta_limit": settings.MAX_DELTA_LIMIT,
            "max_gamma_limit": settings.MAX_GAMMA_LIMIT,
            "max_vega_limit": settings.MAX_VEGA_LIMIT,
            "max_single_trade_loss": 0.02,  # 单笔最大亏损2%
            "max_leverage": 2.0,  # 最大杠杆
            "max_trade_count": 100,  # 最大交易次数
        }
    
    async def start(self):
        """启动风险管理"""
        try:
            logger.info("正在启动风险管理...")
            
            # 初始化每日统计
            await self._initialize_daily_stats()
            
            # 启动检查任务
            self.is_running = True
            self.check_task = asyncio.create_task(self._check_loop())
            
            logger.info("风险管理启动完成")
            
        except Exception as e:
            logger.error(f"风险管理启动失败: {e}")
            raise
    
    async def stop(self):
        """停止风险管理"""
        try:
            logger.info("正在停止风险管理...")
            
            self.is_running = False
            
            # 等待检查任务结束
            if self.check_task:
                self.check_task.cancel()
                try:
                    await self.check_task
                except asyncio.CancelledError:
                    pass
            
            logger.info("风险管理已停止")
            
        except Exception as e:
            logger.error(f"停止风险管理失败: {e}")
    
    async def _initialize_daily_stats(self):
        """初始化每日统计"""
        try:
            today = datetime.now().date()
            
            # 获取今日交易记录
            trades = await db_manager.get_trade_records(limit=1000)
            
            # 计算今日统计
            daily_trades = [
                trade for trade in trades 
                if trade["trade_time"].date() == today
            ]
            
            total_pnl = sum(trade["amount"] for trade in daily_trades)
            trade_count = len(daily_trades)
            
            # 获取当前持仓
            positions = await db_manager.get_positions()
            total_position_value = sum(pos["market_value"] for pos in positions)
            
            self.daily_stats = {
                "date": today,
                "total_pnl": total_pnl,
                "trade_count": trade_count,
                "total_position_value": total_position_value,
                "initial_capital": settings.DEFAULT_CAPITAL,
                "current_capital": settings.DEFAULT_CAPITAL + total_pnl
            }
            
            logger.info(f"每日统计初始化完成: {self.daily_stats}")
            
        except Exception as e:
            logger.error(f"初始化每日统计失败: {e}")
            self.daily_stats = {
                "date": datetime.now().date(),
                "total_pnl": 0.0,
                "trade_count": 0,
                "total_position_value": 0.0,
                "initial_capital": settings.DEFAULT_CAPITAL,
                "current_capital": settings.DEFAULT_CAPITAL
            }
    
    async def _check_loop(self):
        """检查循环"""
        try:
            while self.is_running:
                try:
                    # 执行风险检查
                    await self._perform_risk_checks()
                    
                    # 等待下次检查
                    await asyncio.sleep(settings.RISK_CHECK_INTERVAL)
                    
                except asyncio.CancelledError:
                    break
                except Exception as e:
                    logger.error(f"风险检查循环错误: {e}")
                    await asyncio.sleep(5)  # 出错后等待5秒
                    
        except Exception as e:
            logger.error(f"风险检查循环异常: {e}")
    
    async def _perform_risk_checks(self):
        """执行风险检查"""
        try:
            # 检查仓位限制
            position_risks = await self._check_position_limits()
            
            # 检查日亏损限制
            daily_loss_risks = await self._check_daily_loss_limits()
            
            # 检查希腊值限制
            greeks_risks = await self._check_greeks_limits()
            
            # 检查其他风险
            other_risks = await self._check_other_limits()
            
            # 汇总所有风险
            all_risks = position_risks + daily_loss_risks + greeks_risks + other_risks
            
            # 处理风险告警
            for risk in all_risks:
                if risk.exceeded:
                    await self._handle_risk_alert(risk)
            
            logger.debug(f"风险检查完成，发现 {len([r for r in all_risks if r.exceeded])} 个风险")
            
        except Exception as e:
            logger.error(f"执行风险检查失败: {e}")
    
    async def _check_position_limits(self) -> List[RiskLimit]:
        """检查仓位限制"""
        try:
            risks = []
            
            # 获取当前持仓
            positions = await db_manager.get_positions()
            
            if not positions:
                return risks
            
            # 计算总持仓价值
            total_position_value = sum(pos["market_value"] for pos in positions)
            current_capital = self.daily_stats.get("current_capital", settings.DEFAULT_CAPITAL)
            
            # 计算仓位比例
            position_ratio = total_position_value / current_capital if current_capital > 0 else 0
            
            # 检查仓位限制
            max_position_ratio = self.risk_limits["max_position_size"]
            exceeded = position_ratio > max_position_ratio
            
            risks.append(RiskLimit(
                limit_type="position_size",
                current_value=position_ratio,
                limit_value=max_position_ratio,
                exceeded=exceeded,
                message=f"仓位比例 {position_ratio:.2%} 超过限制 {max_position_ratio:.2%}"
            ))
            
            # 检查杠杆限制
            leverage = total_position_value / current_capital if current_capital > 0 else 0
            max_leverage = self.risk_limits["max_leverage"]
            exceeded = leverage > max_leverage
            
            risks.append(RiskLimit(
                limit_type="leverage",
                current_value=leverage,
                limit_value=max_leverage,
                exceeded=exceeded,
                message=f"杠杆比例 {leverage:.2f} 超过限制 {max_leverage:.2f}"
            ))
            
            return risks
            
        except Exception as e:
            logger.error(f"检查仓位限制失败: {e}")
            return []
    
    async def _check_daily_loss_limits(self) -> List[RiskLimit]:
        """检查日亏损限制"""
        try:
            risks = []
            
            # 获取今日盈亏
            total_pnl = self.daily_stats.get("total_pnl", 0.0)
            initial_capital = self.daily_stats.get("initial_capital", settings.DEFAULT_CAPITAL)
            
            # 计算亏损比例
            loss_ratio = abs(total_pnl) / initial_capital if initial_capital > 0 and total_pnl < 0 else 0
            
            # 检查日亏损限制
            max_daily_loss = self.risk_limits["max_daily_loss"]
            exceeded = loss_ratio > max_daily_loss
            
            risks.append(RiskLimit(
                limit_type="daily_loss",
                current_value=loss_ratio,
                limit_value=max_daily_loss,
                exceeded=exceeded,
                message=f"日亏损比例 {loss_ratio:.2%} 超过限制 {max_daily_loss:.2%}"
            ))
            
            return risks
            
        except Exception as e:
            logger.error(f"检查日亏损限制失败: {e}")
            return []
    
    async def _check_greeks_limits(self) -> List[RiskLimit]:
        """检查希腊值限制"""
        try:
            risks = []
            
            # 获取当前持仓
            positions = await db_manager.get_positions()
            
            if not positions:
                return risks
            
            # 计算总希腊值
            total_delta = sum(pos.get("delta", 0) * pos["quantity"] for pos in positions)
            total_gamma = sum(pos.get("gamma", 0) * pos["quantity"] for pos in positions)
            total_vega = sum(pos.get("vega", 0) * pos["quantity"] for pos in positions)
            
            # 检查Delta限制
            max_delta = self.risk_limits["max_delta_limit"]
            exceeded = abs(total_delta) > max_delta
            
            risks.append(RiskLimit(
                limit_type="delta",
                current_value=total_delta,
                limit_value=max_delta,
                exceeded=exceeded,
                message=f"总Delta {total_delta:.2f} 超过限制 {max_delta:.2f}"
            ))
            
            # 检查Gamma限制
            max_gamma = self.risk_limits["max_gamma_limit"]
            exceeded = abs(total_gamma) > max_gamma
            
            risks.append(RiskLimit(
                limit_type="gamma",
                current_value=total_gamma,
                limit_value=max_gamma,
                exceeded=exceeded,
                message=f"总Gamma {total_gamma:.2f} 超过限制 {max_gamma:.2f}"
            ))
            
            # 检查Vega限制
            max_vega = self.risk_limits["max_vega_limit"]
            exceeded = abs(total_vega) > max_vega
            
            risks.append(RiskLimit(
                limit_type="vega",
                current_value=total_vega,
                limit_value=max_vega,
                exceeded=exceeded,
                message=f"总Vega {total_vega:.2f} 超过限制 {max_vega:.2f}"
            ))
            
            return risks
            
        except Exception as e:
            logger.error(f"检查希腊值限制失败: {e}")
            return []
    
    async def _check_other_limits(self) -> List[RiskLimit]:
        """检查其他限制"""
        try:
            risks = []
            
            # 检查交易次数限制
            trade_count = self.daily_stats.get("trade_count", 0)
            max_trade_count = self.risk_limits["max_trade_count"]
            exceeded = trade_count > max_trade_count
            
            risks.append(RiskLimit(
                limit_type="trade_count",
                current_value=trade_count,
                limit_value=max_trade_count,
                exceeded=exceeded,
                message=f"交易次数 {trade_count} 超过限制 {max_trade_count}"
            ))
            
            return risks
            
        except Exception as e:
            logger.error(f"检查其他限制失败: {e}")
            return []
    
    async def _handle_risk_alert(self, risk: RiskLimit):
        """处理风险告警"""
        try:
            # 确定告警级别
            severity = "medium"
            if risk.limit_type in ["daily_loss", "position_size"]:
                severity = "high"
            
            # 创建告警
            alert = RiskAlert(
                alert_type=risk.limit_type,
                severity=severity,
                message=risk.message,
                data={
                    "current_value": risk.current_value,
                    "limit_value": risk.limit_value,
                    "exceeded": risk.exceeded
                },
                timestamp=datetime.now()
            )
            
            # 保存告警
            async with self._lock:
                self.alerts.append(alert)
            
            # 保存到数据库
            await db_manager.save_risk_alert({
                "alert_type": alert.alert_type,
                "severity": alert.severity,
                "message": alert.message,
                "data": alert.data
            })
            
            # 记录日志
            logger.warning(f"风险告警: {alert.message}")
            
            # 这里可以添加通知逻辑（微信、邮件等）
            # await self._send_notification(alert)
            
        except Exception as e:
            logger.error(f"处理风险告警失败: {e}")
    
    async def check_trade_risk(self, trade_data: Dict) -> Dict[str, Any]:
        """检查交易风险"""
        try:
            # 获取当前风险状态
            current_risks = await self.get_current_risks()
            
            # 检查是否有高风险
            high_risks = [r for r in current_risks if r.exceeded and r.limit_type in ["daily_loss", "position_size"]]
            
            if high_risks:
                return {
                    "allowed": False,
                    "reason": "存在高风险限制",
                    "risks": [{"type": r.limit_type, "message": r.message} for r in high_risks]
                }
            
            # 检查单笔交易风险
            trade_amount = trade_data.get("amount", 0)
            current_capital = self.daily_stats.get("current_capital", settings.DEFAULT_CAPITAL)
            
            # 检查单笔亏损限制
            max_single_loss = self.risk_limits["max_single_trade_loss"]
            potential_loss_ratio = abs(trade_amount) / current_capital if current_capital > 0 else 0
            
            if potential_loss_ratio > max_single_loss:
                return {
                    "allowed": False,
                    "reason": "单笔交易风险过高",
                    "risks": [{
                        "type": "single_trade_loss",
                        "message": f"单笔亏损比例 {potential_loss_ratio:.2%} 超过限制 {max_single_loss:.2%}"
                    }]
                }
            
            return {
                "allowed": True,
                "reason": "",
                "risks": []
            }
            
        except Exception as e:
            logger.error(f"检查交易风险失败: {e}")
            return {"allowed": False, "reason": "风险检查失败", "risks": []}
    
    async def update_daily_stats(self, trade_data: Dict):
        """更新每日统计"""
        try:
            async with self._lock:
                # 更新交易次数
                self.daily_stats["trade_count"] += 1
                
                # 更新盈亏
                amount = trade_data.get("amount", 0)
                self.daily_stats["total_pnl"] += amount
                
                # 更新当前资金
                self.daily_stats["current_capital"] += amount
                
                # 更新持仓价值
                positions = await db_manager.get_positions()
                total_position_value = sum(pos["market_value"] for pos in positions)
                self.daily_stats["total_position_value"] = total_position_value
            
            logger.debug(f"每日统计已更新: {self.daily_stats}")
            
        except Exception as e:
            logger.error(f"更新每日统计失败: {e}")
    
    async def get_current_risks(self) -> List[RiskLimit]:
        """获取当前风险状态"""
        try:
            # 执行所有风险检查
            position_risks = await self._check_position_limits()
            daily_loss_risks = await self._check_daily_loss_limits()
            greeks_risks = await self._check_greeks_limits()
            other_risks = await self._check_other_limits()
            
            return position_risks + daily_loss_risks + greeks_risks + other_risks
            
        except Exception as e:
            logger.error(f"获取当前风险状态失败: {e}")
            return []
    
    def get_risk_limits(self) -> Dict[str, float]:
        """获取风险限制"""
        return self.risk_limits.copy()
    
    async def update_risk_limit(self, limit_type: str, new_value: float) -> bool:
        """更新风险限制"""
        try:
            if limit_type not in self.risk_limits:
                return False
            
            self.risk_limits[limit_type] = new_value
            logger.info(f"风险限制 {limit_type} 已更新为 {new_value}")
            return True
            
        except Exception as e:
            logger.error(f"更新风险限制失败: {e}")
            return False
    
    def get_recent_alerts(self, limit: int = 50) -> List[Dict]:
        """获取最近的告警"""
        try:
            recent_alerts = []
            
            for alert in self.alerts[-limit:]:
                recent_alerts.append({
                    "alert_type": alert.alert_type,
                    "severity": alert.severity,
                    "message": alert.message,
                    "data": alert.data,
                    "timestamp": alert.timestamp.isoformat()
                })
            
            return recent_alerts
            
        except Exception as e:
            logger.error(f"获取最近告警失败: {e}")
            return []


# 全局风险管理器实例
risk_manager = RiskManager()
